msg_tool\scripts\yuris/
ysvr.rs

1//! Yu-Ris YSVR(Variables) file (.ybn)
2use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::*;
6use crate::utils::serde_base64bytes::*;
7use crate::utils::struct_pack::*;
8use anyhow::Result;
9use serde::{Deserialize, Serialize};
10use std::any::Any;
11use std::io::{Read, Seek, Write};
12
13#[derive(Debug, Serialize, Deserialize)]
14struct YSVRData {
15    version: u32,
16    variables: Vec<Variable>,
17}
18
19impl StructUnpack for YSVRData {
20    fn unpack<R: Read + Seek>(
21        reader: &mut R,
22        big: bool,
23        encoding: Encoding,
24        info: &Option<Box<dyn Any>>,
25    ) -> Result<Self> {
26        let version = u32::unpack(reader, big, encoding, info)?;
27        let ninfo = Box::new(version) as Box<dyn Any>;
28        let count = u16::unpack(reader, big, encoding, info)?;
29        let variables = reader.read_struct_vec(count as usize, big, encoding, &Some(ninfo))?;
30        Ok(Self { version, variables })
31    }
32}
33
34impl StructPack for YSVRData {
35    fn pack<W: Write>(
36        &self,
37        writer: &mut W,
38        big: bool,
39        encoding: Encoding,
40        info: &Option<Box<dyn Any>>,
41    ) -> Result<()> {
42        self.version.pack(writer, big, encoding, info)?;
43        let ninfo = Box::new(self.version) as Box<dyn Any>;
44        let count = self.variables.len() as u16;
45        count.pack(writer, big, encoding, info)?;
46        let info = &Some(ninfo);
47        for variable in &self.variables {
48            variable.pack(writer, big, encoding, info)?;
49        }
50        Ok(())
51    }
52}
53
54fn get_info_as_version(info: &Option<Box<dyn Any>>) -> Result<u32> {
55    Ok(*info
56        .as_ref()
57        .ok_or_else(|| anyhow::anyhow!("info not found"))?
58        .downcast_ref()
59        .ok_or_else(|| anyhow::anyhow!("not YSVR version"))?)
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63#[serde(untagged)]
64enum VariableValue {
65    None,
66    Int(i64),
67    Double(f64),
68    ByteString { raw: Base64Bytes },
69    MString(String),
70}
71
72impl StructUnpack for VariableValue {
73    fn unpack<R: Read + Seek>(
74        reader: &mut R,
75        big: bool,
76        encoding: Encoding,
77        info: &Option<Box<dyn Any>>,
78    ) -> Result<Self> {
79        let var_type = *info
80            .as_ref()
81            .and_then(|i| i.downcast_ref::<u8>())
82            .ok_or_else(|| anyhow::anyhow!("VariableValue: type info missing"))?;
83        match var_type {
84            0 => Ok(Self::None),
85            1 => {
86                let v = i64::unpack(reader, big, encoding, info)?;
87                Ok(Self::Int(v))
88            }
89            2 => {
90                let v = f64::unpack(reader, big, encoding, info)?;
91                Ok(Self::Double(v))
92            }
93            3 => {
94                let len = u16::unpack(reader, big, encoding, info)? as usize;
95                let mut buf = vec![0u8; len];
96                reader.read_exact(&mut buf)?;
97                if buf.starts_with(b"M") && buf.len() >= 3 {
98                    let len = u16::from_le_bytes([buf[1], buf[2]]);
99                    if buf.len() >= len as usize + 3 {
100                        if let Ok(s) = decode_to_string(encoding, &buf[3..], true) {
101                            return Ok(Self::MString(s));
102                        }
103                    }
104                }
105                Ok(Self::ByteString { raw: buf.into() })
106            }
107            _ => anyhow::bail!(
108                "Unknown variable type: {} at {}",
109                var_type,
110                reader.stream_position()?
111            ),
112        }
113    }
114}
115
116impl StructPack for VariableValue {
117    fn pack<W: Write>(
118        &self,
119        writer: &mut W,
120        big: bool,
121        encoding: Encoding,
122        info: &Option<Box<dyn Any>>,
123    ) -> Result<()> {
124        match self {
125            Self::Int(v) => v.pack(writer, big, encoding, info)?,
126            Self::Double(v) => v.pack(writer, big, encoding, info)?,
127            Self::ByteString { raw } => {
128                let len = raw.len() as u16;
129                len.pack(writer, big, encoding, info)?;
130                writer.write_all(&raw)?;
131            }
132            Self::MString(s) => {
133                let encoded = encode_string(encoding, &s, true)?;
134                let len = encoded.len() as u16;
135                (len + 3).pack(writer, big, encoding, info)?;
136                writer.write_u8(b'M')?;
137                writer.write_u16(len)?;
138                writer.write_all(&encoded)?;
139            }
140            Self::None => {}
141        }
142        Ok(())
143    }
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
147struct Variable {
148    scope: u8,
149    // #TODO: Better version handle
150    #[serde(skip_serializing_if = "Option::is_none")]
151    unk: Option<u8>,
152    script_id: u16,
153    variable_index: u16,
154    dimension_sizes: Vec<u32>,
155    value: VariableValue,
156}
157
158impl StructUnpack for Variable {
159    fn unpack<R: Read + Seek>(
160        reader: &mut R,
161        big: bool,
162        encoding: Encoding,
163        info: &Option<Box<dyn Any>>,
164    ) -> Result<Self> {
165        let version = get_info_as_version(info)?;
166
167        let scope = reader.read_u8()?;
168        let unk = if version >= 494 {
169            Some(reader.read_u8()?)
170        } else {
171            None
172        };
173        let script_id = reader.read_u16()?;
174        let variable_index = reader.read_u16()?;
175        let variable_type = reader.read_u8()?;
176        let num_dimensions = reader.read_u8()?;
177        let mut dimension_sizes = Vec::with_capacity(num_dimensions as usize);
178        for _ in 0..num_dimensions {
179            dimension_sizes.push(reader.read_u32()?);
180        }
181        let value_info = Box::new(variable_type) as Box<dyn Any>;
182        let value = VariableValue::unpack(reader, big, encoding, &Some(value_info))?;
183
184        Ok(Self {
185            scope,
186            unk,
187            script_id,
188            variable_index,
189            dimension_sizes,
190            value,
191        })
192    }
193}
194
195impl StructPack for Variable {
196    fn pack<W: Write>(
197        &self,
198        writer: &mut W,
199        big: bool,
200        encoding: Encoding,
201        info: &Option<Box<dyn Any>>,
202    ) -> Result<()> {
203        let version = get_info_as_version(info)?;
204
205        writer.write_u8(self.scope)?;
206        if version >= 494 {
207            writer.write_u8(self.unk.unwrap_or(0))?;
208        }
209        writer.write_u16(self.script_id)?;
210        writer.write_u16(self.variable_index)?;
211        writer.write_u8(match &self.value {
212            VariableValue::None => 0,
213            VariableValue::Int(_) => 1,
214            VariableValue::Double(_) => 2,
215            VariableValue::ByteString { .. } => 3,
216            VariableValue::MString(_) => 3,
217        })?;
218        writer.write_u8(self.dimension_sizes.len() as u8)?;
219        for &size in &self.dimension_sizes {
220            writer.write_u32(size)?;
221        }
222        self.value.pack(writer, big, encoding, &None)?;
223        Ok(())
224    }
225}
226
227#[derive(Debug)]
228pub struct YSVRBuilder {}
229
230impl YSVRBuilder {
231    /// Creates a new instance of `YSVRBuilder`
232    pub const fn new() -> Self {
233        YSVRBuilder {}
234    }
235}
236
237impl ScriptBuilder for YSVRBuilder {
238    fn default_encoding(&self) -> Encoding {
239        Encoding::Cp932
240    }
241
242    fn build_script(
243        &self,
244        buf: Vec<u8>,
245        _filename: &str,
246        encoding: Encoding,
247        _archive_encoding: Encoding,
248        config: &ExtraConfig,
249        _archive: Option<&Box<dyn Script>>,
250    ) -> Result<Box<dyn Script + Send + Sync>> {
251        Ok(Box::new(YSVR::new(MemReader::new(buf), encoding, config)?))
252    }
253
254    fn extensions(&self) -> &'static [&'static str] {
255        &["ybn"]
256    }
257
258    fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
259        if buf_len >= 4 && buf.starts_with(b"YSVR") {
260            return Some(20);
261        }
262        None
263    }
264
265    fn script_type(&self) -> &'static ScriptType {
266        &ScriptType::YurisYSVR
267    }
268
269    fn can_create_file(&self) -> bool {
270        true
271    }
272
273    fn create_file<'a>(
274        &'a self,
275        filename: &'a str,
276        writer: Box<dyn WriteSeek + 'a>,
277        encoding: Encoding,
278        file_encoding: Encoding,
279        config: &ExtraConfig,
280    ) -> Result<()> {
281        create_file(
282            filename,
283            writer,
284            encoding,
285            file_encoding,
286            config.custom_yaml,
287        )
288    }
289}
290
291#[derive(Debug)]
292pub struct YSVR {
293    data: YSVRData,
294    custom_yaml: bool,
295}
296
297impl YSVR {
298    pub fn new<T: Read + Seek>(
299        mut reader: T,
300        encoding: Encoding,
301        config: &ExtraConfig,
302    ) -> Result<Self> {
303        let mut sig = [0; 4];
304        reader.read_exact(&mut sig)?;
305        if &sig != b"YSVR" {
306            anyhow::bail!("Unsupported YSVR file.");
307        }
308        let data = YSVRData::unpack(&mut reader, false, encoding, &None)?;
309        Ok(Self {
310            data,
311            custom_yaml: config.custom_yaml,
312        })
313    }
314}
315
316impl Script for YSVR {
317    fn default_output_script_type(&self) -> OutputScriptType {
318        OutputScriptType::Custom
319    }
320
321    fn is_output_supported(&self, output: OutputScriptType) -> bool {
322        matches!(output, OutputScriptType::Custom)
323    }
324
325    fn default_format_type(&self) -> FormatOptions {
326        FormatOptions::None
327    }
328
329    fn custom_output_extension(&self) -> &'static str {
330        if self.custom_yaml { "yaml" } else { "json" }
331    }
332
333    fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
334        let s = if self.custom_yaml {
335            serde_yaml_ng::to_string(&self.data)
336                .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
337        } else {
338            serde_json::to_string_pretty(&self.data)
339                .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
340        };
341        let mut writer = crate::utils::files::write_file(filename)?;
342        let s = encode_string(encoding, &s, false)?;
343        writer.write_all(&s)?;
344        writer.flush()?;
345        Ok(())
346    }
347
348    fn custom_import<'a>(
349        &'a self,
350        custom_filename: &'a str,
351        file: Box<dyn WriteSeek + 'a>,
352        encoding: Encoding,
353        output_encoding: Encoding,
354    ) -> Result<()> {
355        create_file(
356            custom_filename,
357            file,
358            encoding,
359            output_encoding,
360            self.custom_yaml,
361        )
362    }
363}
364
365fn create_file<'a>(
366    custom_filename: &'a str,
367    mut writer: Box<dyn WriteSeek + 'a>,
368    encoding: Encoding,
369    output_encoding: Encoding,
370    yaml: bool,
371) -> Result<()> {
372    let input = crate::utils::files::read_file(custom_filename)?;
373    let s = decode_to_string(output_encoding, &input, true)?;
374    let data: YSVRData = if yaml {
375        serde_yaml_ng::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
376    } else {
377        serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
378    };
379    writer.write_all(b"YSVR")?;
380    data.pack(&mut writer, false, encoding, &None)?;
381    Ok(())
382}